home *** CD-ROM | disk | FTP | other *** search
/ The Arsenal Files 1 / The Arsenal Files (Arsenal Computer).ISO / archive / unixarc.pt4 / text0000.txt < prev   
Encoding:
Text File  |  1994-01-23  |  67.8 KB  |  2,496 lines

  1. Submitted-by: hyc@math.lsa.umich.edu
  2. Posting-number: Volume 15, Issue 80
  3. Archive-name: arc5.21/part04
  4.  
  5. #--------------------------------CUT HERE-------------------------------------
  6. #! /bin/sh
  7. #
  8. # This is a shell archive.  Save this into a file, edit it
  9. # and delete all lines above this comment.  Then give this
  10. # file to sh by executing the command "sh file".  The files
  11. # will be extracted into the current directory owned by
  12. # you with default permissions.
  13. #
  14. # The files contained herein are:
  15. #
  16. # -rw-r--r--  1 hyc         22109 Jun  1 20:01 arclzw.c
  17. # -rw-r--r--  1 hyc          3026 Jun  1 19:41 arcmatch.c
  18. # -rw-r--r--  1 hyc          8418 Jun 13 14:17 arcmisc.c
  19. # -rw-r--r--  1 hyc          7376 Jun  2 16:27 arcpack.c
  20. # -rw-r--r--  1 hyc          3838 Jun  1 19:57 arcrun.c
  21. # -rw-r--r--  1 hyc          1645 Apr 17 18:53 arcs.h
  22. # -rw-------  1 hyc         14613 Jun  2 16:27 arcsq.c
  23. #
  24. echo 'x - arclzw.c'
  25. if test -f arclzw.c; then echo 'shar: not overwriting arclzw.c'; else
  26. sed 's/^X//' << '________This_Is_The_END________' > arclzw.c
  27. X/*
  28. X * $Header: arclzw.c,v 1.4 88/06/01 16:27:59 hyc Locked $
  29. X */
  30. X
  31. X/*
  32. X * ARC - Archive utility - ARCLZW
  33. X * 
  34. X * Version 2.03, created on 10/24/86 at 11:46:22
  35. X * 
  36. X * (C) COPYRIGHT 1985,86 by System Enhancement Associates; ALL RIGHTS RESERVED
  37. X * 
  38. X * By:  Thom Henderson
  39. X * 
  40. X * Description: This file contains the routines used to implement Lempel-Zev
  41. X * data compression, which calls for building a coding table on the fly.
  42. X * This form of compression is especially good for encoding files which
  43. X * contain repeated strings, and can often give dramatic improvements over
  44. X * traditional Huffman SQueezing.
  45. X * 
  46. X * Language: Computer Innovations Optimizing C86
  47. X * 
  48. X * Programming notes: In this section I am drawing heavily on the COMPRESS
  49. X * program from UNIX.  The basic method is taken from "A Technique for High
  50. X * Performance Data Compression", Terry A. Welch, IEEE Computer Vol 17, No 6
  51. X * (June 1984), pp 8-19.  Also see "Knuth's Fundamental Algorithms", Donald
  52. X * Knuth, Vol 3, Section 6.4.
  53. X * 
  54. X * As best as I can tell, this method works by tracing down a hash table of code
  55. X * strings where each entry has the property:
  56. X * 
  57. X * if <string> <char> is in the table then <string> is in the table.
  58. X */
  59. X#include <stdio.h>
  60. X#include "arc.h"
  61. X
  62. Xvoid    putc_pak(), abort(), putc_ncr();
  63. Xint    getc_unp();
  64. X#if    MSDOS
  65. Xchar    *setmem();
  66. X#else
  67. Xchar    *memset();
  68. X#endif
  69. X
  70. Xstatic void    putcode();
  71. X/* definitions for older style crunching */
  72. X
  73. X#define FALSE    0
  74. X#define TRUE     !FALSE
  75. X#define TABSIZE  4096
  76. X#define NO_PRED  0xFFFF
  77. X#define EMPTY    0xFFFF
  78. X#define NOT_FND  0xFFFF
  79. X
  80. Xstatic unsigned short inbuf;    /* partial input code storage */
  81. Xstatic int      sp;        /* current stack pointer */
  82. X
  83. Xstruct entry {        /* string table entry format */
  84. X    char            used;    /* true when this entry is in use */
  85. X    unsigned char   follower;    /* char following string */
  86. X    unsigned short  next;    /* ptr to next in collision list */
  87. X    unsigned short  predecessor;    /* code for preceeding string */
  88. X};            /* string_tab[TABSIZE];    /* the code string table */
  89. X
  90. X
  91. X/* definitions for the new dynamic Lempel-Zev crunching */
  92. X
  93. X#define BITS   12        /* maximum bits per code */
  94. X#define HSIZE  5003        /* 80% occupancy */
  95. X#define INIT_BITS 9        /* initial number of bits/code */
  96. X
  97. Xstatic int      n_bits;        /* number of bits/code */
  98. Xstatic int      maxcode;    /* maximum code, given n_bits */
  99. X#define MAXCODE(n)      ((1<<(n)) - 1)    /* maximum code calculation */
  100. Xstatic int      maxcodemax = 1 << BITS;    /* largest possible code (+1) */
  101. X
  102. Xstatic char     buf[BITS];    /* input/output buffer */
  103. X
  104. Xstatic unsigned char lmask[9] =    /* left side masks */
  105. X{
  106. X 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00
  107. X};
  108. Xstatic unsigned char rmask[9] =    /* right side masks */
  109. X{
  110. X 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff
  111. X};
  112. X
  113. Xstatic int      offset;        /* byte offset for code output */
  114. Xstatic long     in_count;    /* length of input */
  115. Xstatic long     bytes_out;    /* length of compressed output */
  116. Xstatic long     bytes_ref;    /* output quality reference */
  117. Xstatic long     bytes_last;    /* output size at last checkpoint */
  118. Xstatic unsigned short ent;
  119. X
  120. X/*
  121. X * To save much memory (which we badly need at this point), we overlay the
  122. X * table used by the previous version of Lempel-Zev with those used by the
  123. X * new version.  Since no two of these routines will be used together, we can
  124. X * safely do this.
  125. X */
  126. X
  127. Xextern long     htab[HSIZE];        /* hash code table   (crunch) */
  128. Xextern unsigned short codetab[HSIZE];    /* string code table (crunch) */
  129. Xstatic struct    entry *string_tab=(struct entry *)htab;    /* old crunch string table */
  130. X
  131. Xstatic unsigned short *prefix=codetab;    /* prefix code table (uncrunch) */
  132. Xstatic unsigned char *suffix=(unsigned char *)htab;    /* suffix table (uncrunch) */
  133. X
  134. Xstatic int      free_ent;    /* first unused entry */
  135. Xstatic int      firstcmp;    /* true at start of compression */
  136. Xextern unsigned char stack[HSIZE];    /* local push/pop stack */
  137. X
  138. X/*
  139. X * block compression parameters -- after all codes are used up, and
  140. X * compression rate changes, start over.
  141. X */
  142. X
  143. Xstatic int      clear_flg;
  144. X#define CHECK_GAP 2048        /* ratio check interval */
  145. Xstatic long     checkpoint;
  146. Xvoid            upd_tab();
  147. X
  148. X/*
  149. X * the next two codes should not be changed lightly, as they must not lie
  150. X * within the contiguous general code space.
  151. X */
  152. X#define FIRST   257        /* first free entry */
  153. X#define CLEAR   256        /* table clear output code */
  154. X
  155. X/*
  156. X * The cl_block() routine is called at each checkpoint to determine if
  157. X * compression would likely improve by resetting the code table.  The method
  158. X * chosen to determine this is based on empirical observation that, in
  159. X * general, every 2k of input data should compress at least as well as the
  160. X * first 2k of input.
  161. X */
  162. X
  163. Xstatic          void
  164. Xcl_block(t)            /* table clear for block compress */
  165. X    FILE           *t;    /* our output file */
  166. X{
  167. X    checkpoint = in_count + CHECK_GAP;
  168. X
  169. X    if (bytes_ref) {
  170. X        if (bytes_out - bytes_last > bytes_ref) {
  171. X            setmem(htab, HSIZE * sizeof(long), 0xff);
  172. X            free_ent = FIRST;
  173. X            clear_flg = 1;
  174. X            putcode(CLEAR, t);
  175. X            bytes_ref = 0;
  176. X        }
  177. X    } else
  178. X        bytes_ref = bytes_out - bytes_last;
  179. X
  180. X    bytes_last = bytes_out;    /* remember where we were */
  181. X}
  182. X
  183. X/*****************************************************************
  184. X *
  185. X * Output a given code.
  186. X * Inputs:
  187. X *      code:   A n_bits-bit integer.  If == -1, then EOF.  This assumes
  188. X *              that n_bits =< (LONG)wordsize - 1.
  189. X * Outputs:
  190. X *      Outputs code to the file.
  191. X * Assumptions:
  192. X *      Chars are 8 bits long.
  193. X * Algorithm:
  194. X *      Maintain a BITS character long buffer (so that 8 codes will
  195. X * fit in it exactly).  When the buffer fills up empty it and start over.
  196. X */
  197. X
  198. Xstatic          void
  199. Xputcode(code, t)        /* output a code */
  200. X    int             code;    /* code to output */
  201. X    FILE           *t;    /* where to put it */
  202. X{
  203. X    int             r_off = offset;    /* right offset */
  204. X    int             bits = n_bits;    /* bits to go */
  205. X    char           *bp = buf;    /* buffer pointer */
  206. X    int             n;    /* index */
  207. X
  208. X    register int    ztmp;
  209. X
  210. X    if (code >= 0) {    /* if a real code *//* Get to the first byte. */
  211. X        bp += (r_off >> 3);
  212. X        r_off &= 7;
  213. X
  214. X        /*
  215. X         * Since code is always >= 8 bits, only need to mask the
  216. X         * first hunk on the left. 
  217. X         */
  218. X        ztmp = (code << r_off) & lmask[r_off];
  219. X        *bp = (*bp & rmask[r_off]) | ztmp;
  220. X        bp++;
  221. X        bits -= (8 - r_off);
  222. X        code >>= (8 - r_off);
  223. X
  224. X        /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
  225. X        if (bits >= 8) {
  226. X            *bp++ = code;
  227. X            code >>= 8;
  228. X            bits -= 8;
  229. X        }
  230. X        /* Last bits. */
  231. X        if (bits)
  232. X            *bp = code;
  233. X        offset += n_bits;
  234. X
  235. X        if (offset == (n_bits << 3)) {
  236. X            bp = buf;
  237. X            bits = n_bits;
  238. X            bytes_out += bits;
  239. X            do
  240. X                putc_pak(*bp++, t);
  241. X            while (--bits);
  242. X            offset = 0;
  243. X        }
  244. X        /*
  245. X         * If the next entry is going to be too big for the code
  246. X         * size, then increase it, if possible. 
  247. X         */
  248. X        if (free_ent > maxcode || clear_flg > 0) {
  249. X            /*
  250. X             * Write the whole buffer, because the input side
  251. X             * won't discover the size increase until after
  252. X             * it has read it. 
  253. X             */
  254. X            if (offset > 0) {
  255. X                bp = buf;    /* reset pointer for writing */
  256. X                bytes_out += n = n_bits;
  257. X                while (n--)
  258. X                    putc_pak(*bp++, t);
  259. X            }
  260. X            offset = 0;
  261. X
  262. X            if (clear_flg) {    /* reset if clearing */
  263. X                maxcode = MAXCODE(n_bits = INIT_BITS);
  264. X                clear_flg = 0;
  265. X            } else {/* else use more bits */
  266. X                n_bits++;
  267. X                if (n_bits == BITS)
  268. X                    maxcode = maxcodemax;
  269. X                else
  270. X                    maxcode = MAXCODE(n_bits);
  271. X            }
  272. X        }
  273. X    } else {        /* dump the buffer on EOF */
  274. X        bytes_out += n = (offset + 7) / 8;
  275. X
  276. X        if (offset > 0)
  277. X            while (n--)
  278. X                putc_pak(*bp++, t);
  279. X        offset = 0;
  280. X    }
  281. X}
  282. X
  283. X/*****************************************************************
  284. X *
  285. X * Read one code from the standard input.  If EOF, return -1.
  286. X * Inputs:
  287. X *      cmpin
  288. X * Outputs:
  289. X *      code or -1 is returned.
  290. X */
  291. X
  292. Xstatic          int
  293. Xgetcode(f)            /* get a code */
  294. X    FILE           *f;    /* file to get from */
  295. X{
  296. X    int             code;
  297. X    static int      offset = 0, size = 0;
  298. X    int             r_off, bits;
  299. X    unsigned char  *bp = (unsigned char *) buf;
  300. X
  301. X    if (clear_flg > 0 || offset >= size || free_ent > maxcode) {
  302. X        /*
  303. X         * If the next entry will be too big for the current code
  304. X         * size, then we must increase the size. This implies
  305. X         * reading a new buffer full, too. 
  306. X         */
  307. X        if (free_ent > maxcode) {
  308. X            n_bits++;
  309. X            if (n_bits == BITS)
  310. X                maxcode = maxcodemax;    /* won't get any bigger
  311. X                             * now */
  312. X            else
  313. X                maxcode = MAXCODE(n_bits);
  314. X        }
  315. X        if (clear_flg > 0) {
  316. X            maxcode = MAXCODE(n_bits = INIT_BITS);
  317. X            clear_flg = 0;
  318. X        }
  319. X        for (size = 0; size < n_bits; size++) {
  320. X            if ((code = getc_unp(f)) == EOF)
  321. X                break;
  322. X            else
  323. X                buf[size] = (char) code;
  324. X        }
  325. X        if (size <= 0)
  326. X            return -1;    /* end of file */
  327. X
  328. X        offset = 0;
  329. X        /* Round size down to integral number of codes */
  330. X        size = (size << 3) - (n_bits - 1);
  331. X    }
  332. X    r_off = offset;
  333. X    bits = n_bits;
  334. X
  335. X    /*
  336. X     * Get to the first byte. 
  337. X     */
  338. X    bp += (r_off >> 3);
  339. X    r_off &= 7;
  340. X
  341. X    /* Get first part (low order bits) */
  342. X    code = (*bp++ >> r_off);
  343. X    bits -= 8 - r_off;
  344. X    r_off = 8 - r_off;    /* now, offset into code word */
  345. X
  346. X    /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
  347. X    if (bits >= 8) {
  348. X        code |= *bp++ << r_off;
  349. X        r_off += 8;
  350. X        bits -= 8;
  351. X    }
  352. X    /* high order bits. */
  353. X    code |= (*bp & rmask[bits]) << r_off;
  354. X    offset += n_bits;
  355. X
  356. X    return code & MAXCODE(BITS);
  357. X}
  358. X
  359. X/*
  360. X * compress a file
  361. X * 
  362. X * Algorithm:  use open addressing double hashing (no chaining) on the prefix
  363. X * code / next character combination.  We do a variant of Knuth's algorithm D
  364. X * (vol. 3, sec. 6.4) along with G. Knott's relatively-prime secondary probe.
  365. X * Here, the modular division first probe is gives way to a faster
  366. X * exclusive-or manipulation.  Also do block compression with an adaptive
  367. X * reset, where the code table is cleared when the compression ratio
  368. X * decreases, but after the table fills.  The variable-length output codes
  369. X * are re-sized at this point, and a special CLEAR code is generated for the
  370. X * decompressor.
  371. X */
  372. X
  373. Xvoid
  374. Xinit_cm(t)            /* initialize for compression */
  375. X    FILE           *t;    /* where compressed file goes */
  376. X{
  377. X    offset = 0;
  378. X    bytes_out = bytes_last = 1;
  379. X    bytes_ref = 0;
  380. X    clear_flg = 0;
  381. X    in_count = 1;
  382. X    checkpoint = CHECK_GAP;
  383. X    maxcode = MAXCODE(n_bits = INIT_BITS);
  384. X    free_ent = FIRST;
  385. X    setmem(htab, HSIZE * sizeof(long), 0xff);
  386. X    n_bits = INIT_BITS;    /* set starting code size */
  387. X
  388. X    putc_pak(BITS, t);    /* note our max code length */
  389. X
  390. X    firstcmp = 1;        /* next byte will be first */
  391. X}
  392. X
  393. Xvoid
  394. Xputc_cm(c, t)            /* compress a character */
  395. X    unsigned char   c;    /* character to compress */
  396. X    FILE           *t;    /* where to put it */
  397. X{
  398. X    static long     fcode;
  399. X    static int      hshift;
  400. X    int             i;
  401. X    int             disp;
  402. X
  403. X    if (firstcmp) {        /* special case for first byte */
  404. X        ent = c;    /* remember first byte */
  405. X
  406. X        hshift = 0;
  407. X        for (fcode = (long) HSIZE; fcode < 65536L; fcode *= 2L)
  408. X            hshift++;
  409. X        hshift = 8 - hshift;    /* set hash code range bound */
  410. X
  411. X        firstcmp = 0;    /* no longer first */
  412. X        return;
  413. X    }
  414. X    in_count++;
  415. X
  416. X    fcode = (long) (((long) c << BITS) + ent);
  417. X    i = (c << hshift) ^ ent;/* xor hashing */
  418. X
  419. X    if (htab[i] == fcode) {
  420. X        ent = codetab[i];
  421. X        return;
  422. X    } else if (htab[i] < 0)    /* empty slot */
  423. X        goto nomatch;
  424. X    disp = HSIZE - i;    /* secondary hash (after G.Knott) */
  425. X    if (i == 0)
  426. X        disp = 1;
  427. X
  428. Xprobe:
  429. X    if ((i -= disp) < 0)
  430. X        i += HSIZE;
  431. X
  432. X    if (htab[i] == fcode) {
  433. X        ent = codetab[i];
  434. X        return;
  435. X    }
  436. X    if (htab[i] > 0)
  437. X        goto probe;
  438. X
  439. Xnomatch:
  440. X    putcode(ent, t);
  441. X    ent = c;
  442. X    if (free_ent < maxcodemax) {
  443. X        codetab[i] = free_ent++;    /* code -> hashtable */
  444. X        htab[i] = fcode;
  445. X    }
  446. X    if (in_count >= checkpoint)
  447. X        cl_block(t);    /* check for adaptive reset */
  448. X}
  449. X
  450. Xlong
  451. Xpred_cm(t)            /* finish compressing a file */
  452. X    FILE           *t;    /* where to put it */
  453. X{
  454. X    putcode(ent, t);    /* put out the final code */
  455. X    putcode(-1, t);        /* tell output we are done */
  456. X
  457. X    return bytes_out;    /* say how big it got */
  458. X}
  459. X
  460. X/*
  461. X * Decompress a file.  This routine adapts to the codes in the file building
  462. X * the string table on-the-fly; requiring no table to be stored in the
  463. X * compressed file.  The tables used herein are shared with those of the
  464. X * compress() routine.  See the definitions above.
  465. X */
  466. X
  467. Xvoid
  468. Xdecomp(f, t)            /* decompress a file */
  469. X    FILE           *f;    /* file to read codes from */
  470. X    FILE           *t;    /* file to write text to */
  471. X{
  472. X    unsigned char  *stackp;
  473. X    int             finchar;
  474. X    int             code, oldcode, incode;
  475. X
  476. X    if ((code = getc_unp(f)) != BITS)
  477. X        abort("File packed with %d bits, I can only handle %d", code, BITS);
  478. X
  479. X    n_bits = INIT_BITS;    /* set starting code size */
  480. X    clear_flg = 0;
  481. X
  482. X    /*
  483. X     * As above, initialize the first 256 entries in the table. 
  484. X     */
  485. X    maxcode = MAXCODE(n_bits = INIT_BITS);
  486. X    setmem(prefix, 256 * sizeof(short), 0);    /* reset decode string table */
  487. X    for (code = 255; code >= 0; code--)
  488. X        suffix[code] = (unsigned char) code;
  489. X
  490. X    free_ent = FIRST;
  491. X
  492. X    finchar = oldcode = getcode(f);
  493. X    if (oldcode == -1)    /* EOF already? */
  494. X        return;        /* Get out of here */
  495. X    putc_ncr((unsigned char) finchar, t);    /* first code must be 8 bits=char */
  496. X    stackp = stack;
  497. X
  498. X    while ((code = getcode(f)) > -1) {
  499. X        if (code == CLEAR) {    /* reset string table */
  500. X            setmem(prefix, 256 * sizeof(short), 0);
  501. X            clear_flg = 1;
  502. X            free_ent = FIRST - 1;
  503. X            if ((code = getcode(f)) == -1)    /* O, untimely death! */
  504. X                break;
  505. X        }
  506. X        incode = code;
  507. X        /*
  508. X         * Special case for KwKwK string. 
  509. X         */
  510. X        if (code >= free_ent) {
  511. X            if (code > free_ent) {
  512. X                if (warn) {
  513. X                    printf("Corrupted compressed file.\n");
  514. X                    printf("Invalid code %d when max is %d.\n",
  515. X                        code, free_ent);
  516. X                }
  517. X                nerrs++;
  518. X                return;
  519. X            }
  520. X            *stackp++ = finchar;
  521. X            code = oldcode;
  522. X        }
  523. X        /*
  524. X         * Generate output characters in reverse order 
  525. X         */
  526. X        while (code >= 256) {
  527. X            *stackp++ = suffix[code];
  528. X            code = prefix[code];
  529. X        }
  530. X        *stackp++ = finchar = suffix[code];
  531. X
  532. X        /*
  533. X         * And put them out in forward order 
  534. X         */
  535. X        do
  536. X            putc_ncr(*--stackp, t);
  537. X        while (stackp > stack);
  538. X
  539. X        /*
  540. X         * Generate the new entry. 
  541. X         */
  542. X        if ((code = free_ent) < maxcodemax) {
  543. X            prefix[code] = (unsigned short) oldcode;
  544. X            suffix[code] = finchar;
  545. X            free_ent = code + 1;
  546. X        }
  547. X        /*
  548. X         * Remember previous code. 
  549. X         */
  550. X        oldcode = incode;
  551. X    }
  552. X}
  553. X
  554. X
  555. X/*************************************************************************
  556. X * Please note how much trouble it can be to maintain upwards            *
  557. X * compatibility.  All that follows is for the sole purpose of unpacking *
  558. X * files which were packed using an older method.                        *
  559. X *************************************************************************/
  560. X
  561. X
  562. X/*
  563. X * The h() pointer points to the routine to use for calculating a hash value.
  564. X * It is set in the init routines to point to either of oldh() or newh().
  565. X * 
  566. X * oldh() calculates a hash value by taking the middle twelve bits of the square
  567. X * of the key.
  568. X * 
  569. X * newh() works somewhat differently, and was tried because it makes ARC about
  570. X * 23% faster.  This approach was abandoned because dynamic Lempel-Zev
  571. X * (above) works as well, and packs smaller also.  However, inadvertent
  572. X * release of a developmental copy forces us to leave this in.
  573. X */
  574. X
  575. Xstatic unsigned short(*h) ();    /* pointer to hash function */
  576. X
  577. Xstatic unsigned short
  578. Xoldh(pred, foll)        /* old hash function */
  579. X    unsigned short  pred;    /* code for preceeding string */
  580. X    unsigned char   foll;    /* value of following char */
  581. X{
  582. X    long            local;    /* local hash value */
  583. X
  584. X    local = ((pred + foll) | 0x0800) & 0xFFFF; /* create the hash key */
  585. X    local *= local;        /* square it */
  586. X    return (local >> 6) & 0x0FFF;    /* return the middle 12 bits */
  587. X}
  588. X
  589. Xstatic unsigned short
  590. Xnewh(pred, foll)        /* new hash function */
  591. X    unsigned short  pred;    /* code for preceeding string */
  592. X    unsigned char   foll;    /* value of following char */
  593. X{
  594. X    return (((pred + foll) & 0xFFFF) * 15073) & 0xFFF; /* faster hash */
  595. X}
  596. X
  597. X/*
  598. X * The eolist() function is used to trace down a list of entries with
  599. X * duplicate keys until the last duplicate is found.
  600. X */
  601. X
  602. Xstatic unsigned short
  603. Xeolist(index)            /* find last duplicate */
  604. X    unsigned short  index;
  605. X{
  606. X    int             temp;
  607. X
  608. X    while (temp = string_tab[index].next)    /* while more duplicates */
  609. X        index = temp;
  610. X
  611. X    return index;
  612. X}
  613. X
  614. X/*
  615. X * The hash() routine is used to find a spot in the hash table for a new
  616. X * entry.  It performs a "hash and linear probe" lookup, using h() to
  617. X * calculate the starting hash value and eolist() to perform the linear
  618. X * probe.  This routine DOES NOT detect a table full condition.  That MUST be
  619. X * checked for elsewhere.
  620. X */
  621. X
  622. Xstatic unsigned short
  623. Xhash(pred, foll)        /* find spot in the string table */
  624. X    unsigned short  pred;    /* code for preceeding string */
  625. X    unsigned char   foll;    /* char following string */
  626. X{
  627. X    unsigned short  local, tempnext;    /* scratch storage */
  628. X    struct entry   *ep;    /* allows faster table handling */
  629. X
  630. X    local = (*h) (pred, foll);    /* get initial hash value */
  631. X
  632. X    if (!string_tab[local].used)    /* if that spot is free */
  633. X        return local;    /* then that's all we need */
  634. X
  635. X    else {            /* else a collision has occured */
  636. X        local = eolist(local);    /* move to last duplicate */
  637. X
  638. X        /*
  639. X         * We must find an empty spot. We start looking 101 places
  640. X         * down the table from the last duplicate. 
  641. X         */
  642. X
  643. X        tempnext = (local + 101) & 0x0FFF;
  644. X        ep = &string_tab[tempnext];    /* initialize pointer */
  645. X
  646. X        while (ep->used) {    /* while empty spot not found */
  647. X            if (++tempnext == TABSIZE) {    /* if we are at the end */
  648. X                tempnext = 0;    /* wrap to beginning of table */
  649. X                ep = string_tab;
  650. X            } else
  651. X                ++ep;    /* point to next element in table */
  652. X        }
  653. X
  654. X        /*
  655. X         * local still has the pointer to the last duplicate, while
  656. X         * tempnext has the pointer to the spot we found.  We use
  657. X         * this to maintain the chain of pointers to duplicates. 
  658. X         */
  659. X
  660. X        string_tab[local].next = tempnext;
  661. X
  662. X        return tempnext;
  663. X    }
  664. X}
  665. X
  666. X/*
  667. X * The init_tab() routine is used to initialize our hash table. You realize,
  668. X * of course, that "initialize" is a complete misnomer.
  669. X */
  670. X
  671. Xstatic          void
  672. Xinit_tab()
  673. X{                /* set ground state in hash table */
  674. X    unsigned int    i;    /* table index */
  675. X
  676. X    setmem((char *) string_tab, sizeof(string_tab), 0);
  677. X
  678. X    for (i = 0; i < 256; i++)    /* list all single byte strings */
  679. X        upd_tab(NO_PRED, i);
  680. X
  681. X    inbuf = EMPTY;        /* nothing is in our buffer */
  682. X}
  683. X
  684. X/*
  685. X * The upd_tab routine is used to add a new entry to the string table. As
  686. X * previously stated, no checks are made to ensure that the table has any
  687. X * room.  This must be done elsewhere.
  688. X */
  689. X
  690. Xvoid
  691. Xupd_tab(pred, foll)        /* add an entry to the table */
  692. X    unsigned short  pred;    /* code for preceeding string */
  693. X    unsigned short  foll;    /* character which follows string */
  694. X{
  695. X    struct entry   *ep;    /* pointer to current entry */
  696. X
  697. X    /* calculate offset just once */
  698. X
  699. X    ep = &string_tab[hash(pred, foll)];
  700. X
  701. X    ep->used = TRUE;    /* this spot is now in use */
  702. X    ep->next = 0;        /* no duplicates after this yet */
  703. X    ep->predecessor = pred;    /* note code of preceeding string */
  704. X    ep->follower = foll;    /* note char after string */
  705. X}
  706. X
  707. X/*
  708. X * This algorithm encoded a file into twelve bit strings (three nybbles). The
  709. X * gocode() routine is used to read these strings a byte (or two) at a time.
  710. X */
  711. X
  712. Xstatic          int
  713. Xgocode(fd)            /* read in a twelve bit code */
  714. X    FILE           *fd;    /* file to get code from */
  715. X{
  716. X    unsigned short  localbuf, returnval;
  717. X    int             temp;
  718. X
  719. X    if (inbuf == EMPTY) {    /* if on a code boundary */
  720. X        if ((temp = getc_unp(fd)) == EOF)    /* get start of next
  721. X                             * code */
  722. X            return EOF;    /* pass back end of file status */
  723. X        localbuf = temp & 0xFF;    /* mask down to true byte value */
  724. X        if ((temp = getc_unp(fd)) == EOF)
  725. X            /* get end of code, * start of next */
  726. X            return EOF;    /* this should never happen */
  727. X        inbuf = temp & 0xFF;    /* mask down to true byte value */
  728. X
  729. X        returnval = ((localbuf << 4) & 0xFF0) + ((inbuf >> 4) & 0x00F);
  730. X        inbuf &= 0x000F;/* leave partial code pending */
  731. X    } else {        /* buffer contains first nybble */
  732. X        if ((temp = getc_unp(fd)) == EOF)
  733. X            return EOF;
  734. X        localbuf = temp & 0xFF;
  735. X
  736. X        returnval = localbuf + ((inbuf << 8) & 0xF00);
  737. X        inbuf = EMPTY;    /* note no hanging nybbles */
  738. X    }
  739. X    return returnval;    /* pass back assembled code */
  740. X}
  741. X
  742. Xstatic          void
  743. Xpush(c)                /* push char onto stack */
  744. X    int             c;    /* character to push */
  745. X{
  746. X    stack[sp] = ((char) c);    /* coerce integer into a char */
  747. X
  748. X    if (++sp >= TABSIZE)
  749. X        abort("Stack overflow\n");
  750. X}
  751. X
  752. Xstatic          int
  753. Xpop()
  754. X{                /* pop character from stack */
  755. X    if (sp > 0)
  756. X        return ((int) stack[--sp]);    /* leave ptr at next empty
  757. X                         * slot */
  758. X
  759. X    else
  760. X        return EMPTY;
  761. X}
  762. X
  763. X/***** LEMPEL-ZEV DECOMPRESSION *****/
  764. X
  765. Xstatic int      code_count;    /* needed to detect table full */
  766. Xstatic int      firstc;        /* true only on first character */
  767. X
  768. Xvoid
  769. Xinit_ucr(new)            /* get set for uncrunching */
  770. X    int             new;    /* true to use new hash function */
  771. X{
  772. X    if (new)        /* set proper hash function */
  773. X        h = newh;
  774. X    else
  775. X        h = oldh;
  776. X
  777. X    sp = 0;            /* clear out the stack */
  778. X    init_tab();        /* set up atomic code definitions */
  779. X    code_count = TABSIZE - 256;    /* note space left in table */
  780. X    firstc = 1;        /* true only on first code */
  781. X}
  782. X
  783. Xint
  784. Xgetc_ucr(f)            /* get next uncrunched byte */
  785. X    FILE           *f;    /* file containing crunched data */
  786. X{
  787. X    int             code, newcode;
  788. X    static int      oldcode, finchar;
  789. X    struct entry   *ep;    /* allows faster table handling */
  790. X
  791. X    if (firstc) {        /* first code is always known */
  792. X        firstc = FALSE;    /* but next will not be first */
  793. X        oldcode = gocode(f);
  794. X        return finchar = string_tab[oldcode].follower;
  795. X    }
  796. X    if (!sp) {        /* if stack is empty */
  797. X        if ((code = newcode = gocode(f)) == EOF)
  798. X            return EOF;
  799. X
  800. X        ep = &string_tab[code];    /* initialize pointer */
  801. X
  802. X        if (!ep->used) {/* if code isn't known */
  803. X            code = oldcode;
  804. X            ep = &string_tab[code];    /* re-initialize pointer */
  805. X            push(finchar);
  806. X        }
  807. X        while (ep->predecessor != NO_PRED) {
  808. X            push(ep->follower);    /* decode string backwards */
  809. X            code = ep->predecessor;
  810. X            ep = &string_tab[code];
  811. X        }
  812. X
  813. X        push(finchar = ep->follower);    /* save first character also */
  814. X
  815. X        /*
  816. X         * The above loop will terminate, one way or another, with
  817. X         * string_tab[code].follower equal to the first character in
  818. X         * the string. 
  819. X         */
  820. X
  821. X        if (code_count) {    /* if room left in string table */
  822. X            upd_tab(oldcode, finchar);
  823. X            --code_count;
  824. X        }
  825. X        oldcode = newcode;
  826. X    }
  827. X    return pop();        /* return saved character */
  828. X}
  829. ________This_Is_The_END________
  830. if test `wc -c < arclzw.c` -ne    22109; then
  831.     echo 'shar: arclzw.c was damaged during transit (should have been    22109 bytes)'
  832. fi
  833. fi        ; : end of overwriting check
  834. echo 'x - arcmatch.c'
  835. if test -f arcmatch.c; then echo 'shar: not overwriting arcmatch.c'; else
  836. sed 's/^X//' << '________This_Is_The_END________' > arcmatch.c
  837. X/*
  838. X * $Header: arcmatch.c,v 1.5 88/06/01 19:41:08 hyc Locked $
  839. X */
  840. X
  841. X/*
  842. X * ARC - Archive utility - ARCMATCH
  843. X * 
  844. X * Version 2.17, created on 12/17/85 at 20:32:18
  845. X * 
  846. X * (C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED
  847. X * 
  848. X * By:  Thom Henderson
  849. X * 
  850. X * Description: This file contains service routines needed to maintain an
  851. X * archive.
  852. X * 
  853. X * Language: Computer Innovations Optimizing C86
  854. X */
  855. X#include <stdio.h>
  856. X#include "arc.h"
  857. X
  858. Xint    strcmp(), strlen();
  859. Xvoid    abort();
  860. Xchar    *strcpy();
  861. X
  862. Xint
  863. Xmatch(n, t)            /* test name against template */
  864. X    char           *n;    /* name to test */
  865. X    char           *t;    /* template to test against */
  866. X{
  867. X#if    MTS
  868. X    fortran         patbuild(), patmatch(), patfree();
  869. X    static int      patlen = (-1);
  870. X    static int      patwork = 0;
  871. X    static int      patswch = 0;
  872. X    char            patccid[4];
  873. X    static char     patchar[2] = "?";
  874. X    static char     oldtemp[16] = 0;
  875. X    int             namlen, RETCODE;
  876. X
  877. X    if (strcmp(t, oldtemp)) {
  878. X        if (patwork)
  879. X            patfree(&patwork);
  880. X        strcpy(oldtemp, t);
  881. X        patlen = strlen(oldtemp);
  882. X        patbuild(oldtemp, &patlen, &patwork, &patswch, patccid, patchar, _retcode RETCODE);
  883. X        if (RETCODE > 8) {
  884. X            printf("MTS: patbuild returned %d\n", RETCODE);
  885. X            abort("bad wildcard in filename");
  886. X        }
  887. X    }
  888. X    namlen = strlen(n);
  889. X    patmatch(n, &namlen, &patwork, _retcode RETCODE);
  890. X    switch (RETCODE) {
  891. X    case 0:
  892. X        return (1);
  893. X    case 4:
  894. X        return (0);
  895. X    default:
  896. X        abort("wildcard pattern match failed");
  897. X    }
  898. X}
  899. X
  900. X#else
  901. X#if    !UNIX
  902. X    upper(n);
  903. X    upper(t);        /* avoid case problems */
  904. X#endif    /* UNIX */
  905. X#if    GEMDOS
  906. X    return(pnmatch(n, t, 0));
  907. X#else
  908. X    /* first match name part */
  909. X
  910. X    while ((*n && *n != '.') || (*t && *t != '.')) {
  911. X        if (*n != *t && *t != '?') {    /* match fail? */
  912. X            if (*t != '*')    /* wildcard fail? */
  913. X                return 0;    /* then no match */
  914. X            else {    /* else jump over wildcard */
  915. X                while (*n && *n != '.')
  916. X                    n++;
  917. X                while (*t && *t != '.')
  918. X                    t++;
  919. X                break;    /* name part matches wildcard */
  920. X            }
  921. X        } else {    /* match good for this char */
  922. X            n++;    /* advance to next char */
  923. X            t++;
  924. X        }
  925. X    }
  926. X
  927. X    if (*n && *n == '.')
  928. X        n++;        /* skip extension delimiters */
  929. X    if (*t && *t == '.')
  930. X        t++;
  931. X
  932. X    /* now match name part */
  933. X
  934. X    while (*n || *t) {
  935. X        if (*n != *t && *t != '?') {    /* match fail? */
  936. X            if (*t != '*')    /* wildcard fail? */
  937. X                return 0;    /* then no match */
  938. X            else
  939. X                return 1;    /* else good enough */
  940. X        } else {    /* match good for this char */
  941. X            n++;    /* advance to next char */
  942. X            t++;
  943. X        }
  944. X    }
  945. X
  946. X    return 1;        /* match worked */
  947. X#endif    /* GEMDOS */
  948. X}
  949. X#endif
  950. X
  951. Xvoid
  952. Xrempath(nargs, arg)        /* remove paths from filenames */
  953. X    int             nargs;    /* number of names */
  954. X    char           *arg[];    /* pointers to names */
  955. X{
  956. X    char           *i, *rindex();    /* string index, reverse indexer */
  957. X    int             n;    /* index */
  958. X
  959. X    for (n = 0; n < nargs; n++) {    /* for each supplied name */
  960. X        if (!(i = rindex(arg[n], '\\')))    /* search for end of
  961. X                             * path */
  962. X            if (!(i = rindex(arg[n], '/')))
  963. X                i = rindex(arg[n], ':');
  964. X        if (i)        /* if path was found */
  965. X            arg[n] = i + 1;    /* then skip it */
  966. X    }
  967. X}
  968. ________This_Is_The_END________
  969. if test `wc -c < arcmatch.c` -ne     3026; then
  970.     echo 'shar: arcmatch.c was damaged during transit (should have been     3026 bytes)'
  971. fi
  972. fi        ; : end of overwriting check
  973. echo 'x - arcmisc.c'
  974. if test -f arcmisc.c; then echo 'shar: not overwriting arcmisc.c'; else
  975. sed 's/^X//' << '________This_Is_The_END________' > arcmisc.c
  976. X/*
  977. X * Miscellaneous routines to get ARC running on non-MSDOS systems...
  978. X * $Header: arcmisc.c,v 1.7 88/06/12 19:23:13 hyc Locked $ 
  979. X */
  980. X
  981. X#include <stdio.h>
  982. X#include <ctype.h>
  983. X#include "arc.h"
  984. X
  985. X#if    MSDOS
  986. X#include <dir.h>
  987. X#include <stat.h>
  988. X#endif
  989. X
  990. X#if    GEMDOS
  991. X#include <osbind.h>
  992. X#include <stat.h>
  993. Xchar           *index(), *rindex();
  994. X
  995. Xvoid 
  996. Xexitpause()
  997. X{
  998. X    while (Cconis())
  999. X        Cnecin();
  1000. X    fprintf(stderr, "Press any key to continue: ");
  1001. X    fflush(stderr);
  1002. X    Cnecin();
  1003. X    fprintf(stderr, "\n");
  1004. X}
  1005. X
  1006. Xint
  1007. Xchdir(dirname)
  1008. X    char           *dirname;
  1009. X{
  1010. X    char           *i;
  1011. X    int             drv;
  1012. X
  1013. X    i = dirname;
  1014. X    if ((i = index(dirname, ':')) != NULL) {
  1015. X        drv = i[-1];
  1016. X        i++;        /* Move past device spec */
  1017. X        if (drv > '\'')
  1018. X            drv -= 'a';
  1019. X        else
  1020. X            drv -= 'A';
  1021. X        if (drv >= 0 && drv < 16)
  1022. X            Dsetdrv(drv);
  1023. X    }
  1024. X    if (*i != '\0')
  1025. X        return (Dsetpath(i));
  1026. X}
  1027. X#endif
  1028. X
  1029. X#if    UNIX
  1030. X#include <sys/types.h>
  1031. X#include <sys/dir.h>
  1032. X#include <sys/stat.h>
  1033. X    int    rename();
  1034. X#endif
  1035. X
  1036. X#if    SYSV
  1037. X#include <dirent.h>
  1038. X#define DIRECT dirent
  1039. X#else
  1040. X#define DIRECT direct
  1041. X#endif
  1042. X
  1043. Xchar           *strcpy(), *strcat(), *malloc();
  1044. Xint             strlen();
  1045. X
  1046. Xint
  1047. Xmove(oldnam, newnam)
  1048. X    char           *oldnam, *newnam;
  1049. X{
  1050. X    FILE           *fopen(), *old, *new;
  1051. X    struct stat     oldstat;
  1052. X    char           *strcpy();
  1053. X    void        filecopy();
  1054. X#if    GEMDOS
  1055. X    if (Frename(0, oldnam, newnam))
  1056. X#else
  1057. X    if (rename(oldnam, newnam))
  1058. X#endif
  1059. X    {
  1060. X        if (stat(oldnam, &oldstat))    /* different partition? */
  1061. X            return (-1);
  1062. X        old = fopen(oldnam, "rb");
  1063. X        if (old == NULL)
  1064. X            return (-1);
  1065. X        new = fopen(newnam, "wb");
  1066. X        if (new == NULL)
  1067. X            return (-1);
  1068. X        filecopy(old, new, oldstat.st_size);
  1069. X        unlink(oldnam);
  1070. X    }
  1071. X    return 0;
  1072. X}
  1073. X
  1074. Xstatic void
  1075. X_makefn(source, dest)
  1076. X    char           *source;
  1077. X    char           *dest;
  1078. X{
  1079. X    int             j;
  1080. X#if    MSDOS
  1081. X    char           *setmem();
  1082. X#else
  1083. X    char           *memset();
  1084. X#endif
  1085. X
  1086. X    setmem(dest, 17, 0);    /* clear result field */
  1087. X    for (j = 0; *source && *source != '.'; ++source)
  1088. X        if (j < 8)
  1089. X            dest[j++] = *source;
  1090. X    for (j = 9; *source; ++source)
  1091. X        if (j < 13)
  1092. X            dest[j++] = *source;
  1093. X}
  1094. X/*
  1095. X * make a file name using a template 
  1096. X */
  1097. X
  1098. Xchar           *
  1099. Xmakefnam(rawfn, template, result)
  1100. X    char           *rawfn;    /* the original file name */
  1101. X    char           *template;    /* the template data */
  1102. X    char           *result;    /* where to place the result */
  1103. X{
  1104. X    char            et[17], er[17], rawbuf[STRLEN], *i, *rindex();
  1105. X
  1106. X    *rawbuf = 0;
  1107. X    strcpy(rawbuf, rawfn);
  1108. X#if    MTS
  1109. X    i = rawbuf;
  1110. X    if (rawbuf[0] == tmpchr[0]) {
  1111. X        i++;
  1112. X        strcpy(rawfn, i);
  1113. X    } else
  1114. X#endif
  1115. X    if ((i = rindex(rawbuf, CUTOFF))) {
  1116. X        i++;
  1117. X        strcpy(rawfn, i);
  1118. X    }
  1119. X#if    DOS
  1120. X    else if ((i = rindex(rawbuf, ':'))) {
  1121. X        i++;
  1122. X        strcpy(rawfn, i);
  1123. X    }
  1124. X#endif
  1125. X    if (i)
  1126. X        *i = 0;
  1127. X    else
  1128. X        *rawbuf = 0;
  1129. X
  1130. X    _makefn(template, et);
  1131. X    _makefn(rawfn, er);
  1132. X    *result = 0;        /* assure no data */
  1133. X    strcat(result, rawbuf);
  1134. X    strcat(result, er[0] ? er : et);
  1135. X    strcat(result, er[9] ? er + 9 : et + 9);
  1136. X    return ((char *) &result[0]);
  1137. X}
  1138. X
  1139. X#if    MSDOS || SYSV
  1140. X
  1141. Xint
  1142. Xalphasort(dirptr1, dirptr2)
  1143. X    struct DIRECT **dirptr1, **dirptr2;
  1144. X{
  1145. X    return (strcmp((*dirptr1)->d_name, (*dirptr2)->d_name));
  1146. X}
  1147. X
  1148. X#endif
  1149. X
  1150. Xvoid
  1151. Xupper(string)
  1152. X    char           *string;
  1153. X{
  1154. X    char           *p;
  1155. X
  1156. X    for (p = string; *p; p++)
  1157. X        if (islower(*p))
  1158. X            *p = toupper(*p);
  1159. X}
  1160. X/* VARARGS1 */
  1161. Xvoid
  1162. Xabort(s, arg1, arg2, arg3)
  1163. X    char           *s;
  1164. X{
  1165. X    fprintf(stderr, "ARC: ");
  1166. X    fprintf(stderr, s, arg1, arg2, arg3);
  1167. X    fprintf(stderr, "\n");
  1168. X#if    UNIX
  1169. X    perror("UNIX");
  1170. X#endif
  1171. X#if    GEMDOS
  1172. X    exitpause();
  1173. X#endif
  1174. X    exit(1);
  1175. X}
  1176. X
  1177. X#if    !MTS
  1178. X
  1179. Xchar           *
  1180. Xgcdir(dirname)
  1181. X    char           *dirname;
  1182. X
  1183. X{
  1184. X    char           *getwd();
  1185. X#if    GEMDOS
  1186. X    int             drv;
  1187. X    char           *buf;
  1188. X#endif
  1189. X    if (dirname == NULL || strlen(dirname) == 0)
  1190. X        dirname = (char *) malloc(1024);
  1191. X
  1192. X#if    !GEMDOS
  1193. X    getwd(dirname);
  1194. X#else
  1195. X    buf = dirname;
  1196. X    *buf++ = (drv = Dgetdrv()) + 'A';
  1197. X    *buf++ = ':';
  1198. X    Dgetpath(buf, 0);
  1199. X#endif
  1200. X    return (dirname);
  1201. X}
  1202. X
  1203. X#if    UNIX
  1204. Xchar           *pattern;    /* global so that fmatch can use it */
  1205. X#endif
  1206. X
  1207. Xchar           *
  1208. Xdir(filename)        /* get files, one by one */
  1209. X    char           *filename;    /* template, or NULL */
  1210. X{
  1211. X#if    GEMDOS
  1212. X    static int      Nnum = 0;
  1213. X    static DMABUFFER dbuf, *saved;
  1214. X    char           *name;
  1215. X
  1216. X    if (Nnum == 0) {    /* first call */
  1217. X        saved = (DMABUFFER *) Fgetdta();
  1218. X        Fsetdta(&dbuf);
  1219. X        if (Fsfirst(filename, 0) == 0) {
  1220. X            name = malloc(FNLEN);
  1221. X            strcpy(name, dbuf.d_fname);
  1222. X            Nnum++;
  1223. X            return (name);
  1224. X        } else {
  1225. X            Fsetdta(saved);
  1226. X            return (NULL);
  1227. X        }
  1228. X    } else {
  1229. X        if (Fsnext() == 0) {
  1230. X            name = malloc(FNLEN);
  1231. X            strcpy(name, dbuf.d_fname);
  1232. X            return (name);
  1233. X        } else {
  1234. X            Nnum = 0;
  1235. X            Fsetdta(saved);
  1236. X            return (NULL);
  1237. X        }
  1238. X    }
  1239. X}
  1240. X#else
  1241. X    static struct DIRECT **namelist;
  1242. X    static char   **NameList;
  1243. X    static char    namecopy[STRLEN], *dirname;
  1244. X#if    UNIX
  1245. X    int             alphasort();
  1246. X    int             scandir();
  1247. X#endif                /* UNIX */
  1248. X    int             fmatch();
  1249. X    static int      Nnum = 0, ii;
  1250. X    char        *rindex();
  1251. X
  1252. X
  1253. X    if (Nnum == 0) {    /* first call */
  1254. X        strcpy(namecopy,filename);
  1255. X        if(pattern=rindex(namecopy,CUTOFF)) {
  1256. X            *pattern = 0;
  1257. X            pattern++;
  1258. X            dirname = namecopy;
  1259. X        } else {
  1260. X            pattern = filename;
  1261. X            dirname = ".";
  1262. X        }
  1263. X        Nnum = scandir(dirname, &namelist, fmatch, alphasort);
  1264. X        NameList = (char **) malloc(Nnum * sizeof(char *));
  1265. X        for (ii = 0; ii < Nnum; ii++) {
  1266. X            (NameList)[ii] = malloc(strlen(namelist[ii]->d_name) + 1);
  1267. X            strcpy((NameList)[ii], namelist[ii]->d_name);
  1268. X        }
  1269. X        ii = 0;
  1270. X    }
  1271. X    if (ii >= Nnum) {    /* all out of files */
  1272. X        if (Nnum) {    /* there were some files found */
  1273. X            for (ii = 0; ii < Nnum; ii++)
  1274. X                free(namelist[ii]);
  1275. X            free(namelist);
  1276. X        }
  1277. X        Nnum = 0;
  1278. X        return (NULL);
  1279. X    } else {
  1280. X        return ((NameList)[ii++]);
  1281. X    }
  1282. X}
  1283. X
  1284. X/*
  1285. X * Filename match - here, * matches everything 
  1286. X */
  1287. X
  1288. Xint
  1289. Xfmatch(direntry)
  1290. X    struct DIRECT  *direntry;
  1291. X{
  1292. X    char           *string;
  1293. X
  1294. X    string = direntry->d_name;
  1295. X
  1296. X    if (!strcmp(pattern, "") || !strcmp(pattern, "*.*") || !strcmp(pattern, "*"))
  1297. X        return (1);
  1298. X    return (match(string, pattern));
  1299. X}
  1300. X#endif                /* GEMDOS */
  1301. X#else
  1302. X/* dir code for MTS under Bell Labs C... */
  1303. X
  1304. Xchar           *
  1305. Xdir(filepattern)
  1306. X    char           *filepattern;    /* template or NULL */
  1307. X{
  1308. X    char           *malloc(), *index();
  1309. X#if    USECATSCAN
  1310. X    fortran void    catscan(), fileinfo();
  1311. X
  1312. X    struct catname {
  1313. X        short           len;
  1314. X        char            name[257];
  1315. X    }               pattern;
  1316. X
  1317. X    struct catval {
  1318. X        int             maxlen;
  1319. X        int             actlen;
  1320. X        char            name[257];
  1321. X    }               catreturn;
  1322. X
  1323. X    char           *i;
  1324. X    int             j, RETCODE;
  1325. X
  1326. X    static int      catptr = 0;
  1327. X    static int      catflag = 0x200;
  1328. X    static int      cattype = 1;
  1329. X    static int      patflag = 0;
  1330. X
  1331. X    catreturn.maxlen = 256;
  1332. X
  1333. X    if (patflag) {
  1334. X        patflag = 0;
  1335. X        catptr = 0;
  1336. X        return (NULL);
  1337. X    }
  1338. X    if (filepattern) {
  1339. X        strcpy(pattern.name, filepattern);
  1340. X        pattern.len = strlen(filepattern);
  1341. X        if (!index(filepattern, '?'))
  1342. X            patflag = 1;
  1343. X    }
  1344. X    if (patflag) {
  1345. X        fileinfo(&pattern, &cattype, "CINAME  ", &catreturn, _retcode RETCODE);
  1346. X        catptr = RETCODE ? 0 : 1;
  1347. X    } else
  1348. X        catscan(&pattern, &catflag, &cattype, &catreturn, &catptr);
  1349. X
  1350. X    if (!catptr)
  1351. X        return (NULL);
  1352. X    else {
  1353. X        char           *k;
  1354. X
  1355. X        k = index(catreturn.name, ' ');
  1356. X        if (k)
  1357. X            *k = 0;
  1358. X        else {
  1359. X            j = catreturn.actlen;
  1360. X            catreturn.name[j] = 0;
  1361. X        }
  1362. X        k = catreturn.name;
  1363. X        if (catreturn.name[0] == tmpchr[0])
  1364. X            k++;
  1365. X        else if ((k = index(catreturn.name, sepchr[0])))
  1366. X            k++;
  1367. X        else
  1368. X            k = catreturn.name;
  1369. X        j = strlen(k);
  1370. X        i = malloc(++j);
  1371. X        strcpy(i, k);
  1372. X        return (i);
  1373. X    }
  1374. X#else
  1375. X    fortran void    gfinfo();
  1376. X    static char     gfname[24];
  1377. X    static char     pattern[20];
  1378. X    static int      gfdummy[2] = {
  1379. X                      0, 0
  1380. X    },              gfflags;
  1381. X    int             i, RETCODE;
  1382. X    char           *j, *k;
  1383. X
  1384. X    if (filepattern) {
  1385. X        strcpy(pattern, filepattern);
  1386. X        strcat(pattern, " ");
  1387. X        for (i = 20; i < 24; i++)
  1388. X            gfname[i] = '\0';
  1389. X        if (index(pattern, '?'))
  1390. X            gfflags = 0x0C;
  1391. X        else
  1392. X            gfflags = 0x09;
  1393. X    } else if (gfflags == 0x09)
  1394. X        return (NULL);
  1395. X
  1396. X    gfinfo(pattern, gfname, &gfflags, gfdummy, gfdummy, gfdummy, _retcode RETCODE);
  1397. X    if (RETCODE)
  1398. X        return (NULL);
  1399. X    else {
  1400. X        k = index(gfname, ' ');
  1401. X        *k = '\0';
  1402. X        k = gfname;
  1403. X        if (gfname[0] == tmpchr[0])
  1404. X            k++;
  1405. X        else if ((k = index(gfname, sepchr[0])))
  1406. X            k++;
  1407. X        else
  1408. X            k = gfname;
  1409. X        i = strlen(k);
  1410. X        j = malloc(++i);
  1411. X        strcpy(j, k);
  1412. X        return (j);
  1413. X    }
  1414. X#endif
  1415. X}
  1416. X
  1417. Xint
  1418. Xunlink(path)
  1419. X    char           *path;    /* name of file to delete */
  1420. X{
  1421. X    fortran void    destroy();
  1422. X    int             RETCODE;
  1423. X
  1424. X    char            name[258];
  1425. X
  1426. X    strcpy(name, path);
  1427. X    strcat(name, " ");
  1428. X    destroy(name, _retcode RETCODE);
  1429. X    if (RETCODE)
  1430. X        return (-1);
  1431. X    else
  1432. X        return (0);
  1433. X}
  1434. X#endif
  1435. ________This_Is_The_END________
  1436. if test `wc -c < arcmisc.c` -ne     8418; then
  1437.     echo 'shar: arcmisc.c was damaged during transit (should have been     8418 bytes)'
  1438. fi
  1439. fi        ; : end of overwriting check
  1440. echo 'x - arcpack.c'
  1441. if test -f arcpack.c; then echo 'shar: not overwriting arcpack.c'; else
  1442. sed 's/^X//' << '________This_Is_The_END________' > arcpack.c
  1443. X/*
  1444. X * $Header: arcpack.c,v 1.9 88/06/02 16:27:36 hyc Locked $
  1445. X */
  1446. X
  1447. X/*  ARC - Archive utility - ARCPACK
  1448. X
  1449. X    Version 3.49, created on 04/21/87 at 11:26:51
  1450. X
  1451. X(C) COPYRIGHT 1985-87 by System Enhancement Associates; ALL RIGHTS RESERVED
  1452. X
  1453. X    By:     Thom Henderson
  1454. X
  1455. X    Description:
  1456. X     This file contains the routines used to compress a file
  1457. X     when placing it in an archive.
  1458. X
  1459. X    Language:
  1460. X     Computer Innovations Optimizing C86
  1461. X*/
  1462. X#include <stdio.h>
  1463. X#include "arc.h"
  1464. X#if    MTS
  1465. X#include <ctype.h>
  1466. X#endif
  1467. X
  1468. Xvoid    setcode(), sqinit_cm(), sqputc_cm(), init_cm(), putc_cm();
  1469. Xvoid    filecopy(), abort(), putc_tst();
  1470. Xint    getch(), addcrc();
  1471. X
  1472. X/* stuff for non-repeat packing */
  1473. X
  1474. X#define DLE 0x90        /* repeat sequence marker */
  1475. X
  1476. Xstatic unsigned char state;    /* current packing state */
  1477. X
  1478. X/* non-repeat packing states */
  1479. X
  1480. X#define NOHIST  0        /* don't consider previous input */
  1481. X#define SENTCHAR 1        /* lastchar set, no lookahead yet */
  1482. X#define SENDNEWC 2        /* run over, send new char next */
  1483. X#define SENDCNT 3        /* newchar set, send count next */
  1484. X
  1485. X/* packing results */
  1486. X
  1487. Xstatic long     stdlen;        /* length for standard packing */
  1488. Xstatic short    crcval;        /* CRC check value */
  1489. X
  1490. Xvoid
  1491. Xpack(f, t, hdr)            /* pack file into an archive */
  1492. X    FILE           *f, *t;    /* source, destination */
  1493. X    struct heads   *hdr;    /* pointer to header data */
  1494. X{
  1495. X    int             c;    /* one character of stream */
  1496. X    long            ncrlen;    /* length after packing */
  1497. X    long        huflen;    /* length after squeezing */
  1498. X    long            lzwlen;    /* length after crunching */
  1499. X    long        pred_sq(), file_sq();    /* stuff for squeezing */
  1500. X    long            pred_cm(), sqpred_cm();    /* dynamic crunching cleanup */
  1501. X    long            tloc, ftell();    /* start of output */
  1502. X    int        getch();
  1503. X    int             getc_ncr();
  1504. X    void            putc_pak();
  1505. X
  1506. X    /* first pass - see which method is best */
  1507. X
  1508. X    tloc = ftell(t);    /* note start of output */
  1509. X
  1510. X    if (!nocomp) {        /* if storage kludge not active */
  1511. X        if (note) {
  1512. X            printf(" analyzing, ");
  1513. X            fflush(stdout);
  1514. X        }
  1515. X        state = NOHIST;    /* initialize ncr packing */
  1516. X        stdlen = ncrlen = 0;    /* reset size counters */
  1517. X        crcval = 0;    /* initialize CRC check value */
  1518. X        setcode();    /* initialize encryption */
  1519. X        init_sq();    /* initialize for squeeze scan */
  1520. X
  1521. X        if (dosquash) {
  1522. X            sqinit_cm();
  1523. X            while ((c = getch(f)) != EOF) {    /* for each byte of file */
  1524. X                ncrlen++;    /* one more packed byte */
  1525. X                scan_sq(c);    /* see what squeezing can do */
  1526. X                sqputc_cm(c, t);    /* see what squashing
  1527. X                             * can do */
  1528. X            }
  1529. X            lzwlen = sqpred_cm(t);
  1530. X        } else {
  1531. X            init_cm(t);    /* initialize for crunching */
  1532. X    
  1533. X            while ((c = getc_ncr(f)) != EOF) {    /* for each byte of file */
  1534. X                ncrlen++;    /* one more packed byte */
  1535. X                scan_sq(c);    /* see what squeezing can do */
  1536. X                putc_cm(c, t);    /* see what crunching can do */
  1537. X            }
  1538. X            lzwlen = pred_cm(t);    /* finish up after crunching */
  1539. X        }
  1540. X        huflen = pred_sq();    /* finish up after squeezing */
  1541. X    } else {        /* else kludge the method */
  1542. X        stdlen = 0;    /* make standard look best */
  1543. X        ncrlen = huflen = lzwlen = 1;
  1544. X    }
  1545. X
  1546. X    /* standard set-ups common to all methods */
  1547. X
  1548. X    fseek(f, 0L, 0);    /* rewind input */
  1549. X    hdr->crc = crcval;    /* note CRC check value */
  1550. X    hdr->length = stdlen;    /* set actual file length */
  1551. X    state = NOHIST;        /* reinitialize ncr packing */
  1552. X    setcode();        /* reinitialize encryption */
  1553. X
  1554. X    /* choose and use the shortest method */
  1555. X
  1556. X    if (kludge && note)
  1557. X        printf("\n\tS:%ld  P:%ld  S:%ld  C:%ld,\t ",
  1558. X            stdlen, ncrlen, huflen, lzwlen);
  1559. X
  1560. X    if (stdlen <= ncrlen && stdlen <= huflen && stdlen <= lzwlen) {
  1561. X        if (note) {
  1562. X            printf("storing, ");    /* store without compression */
  1563. X            fflush(stdout);
  1564. X        }
  1565. X        hdrver = 2;    /* note packing method */
  1566. X        fseek(t, tloc, 0);    /* reset output for new method */
  1567. X        stdlen = crcval = 0;    /* recalc these for kludge */
  1568. X        while ((c = getch(f)) != EOF)    /* store it straight */
  1569. X            putc_pak(c, t);
  1570. X        hdr->crc = crcval;
  1571. X        hdr->length = hdr->size = stdlen;
  1572. X    } else if (ncrlen < lzwlen && ncrlen < huflen) {
  1573. X        if (note) {
  1574. X            printf("packing, ");    /* pack with repeat
  1575. X            fflush(stdout);         * suppression */
  1576. X        }
  1577. X        hdrver = 3;    /* note packing method */
  1578. X        hdr->size = ncrlen;    /* set data length */
  1579. X        fseek(t, tloc, 0);    /* reset output for new method */
  1580. X        while ((c = getc_ncr(f)) != EOF)
  1581. X            putc_pak(c, t);
  1582. X    } else if (huflen < lzwlen) {
  1583. X        if (note) {
  1584. X            printf("squeezing, ");
  1585. X            fflush(stdout);
  1586. X        }
  1587. X        hdrver = 4;    /* note packing method */
  1588. X        fseek(t, tloc, 0);    /* reset output for new method */
  1589. X        hdr->size = file_sq(f, t);    /* note final size */
  1590. X    } else {
  1591. X        if (note)
  1592. X            printf(dosquash ? "squashed, " : "crunched, ");
  1593. X        hdrver = dosquash ? 9 : 8;
  1594. X        hdr->size = lzwlen;    /* size should not change */
  1595. X    }
  1596. X
  1597. X    /* standard cleanups common to all methods */
  1598. X
  1599. X    if (note)
  1600. X        printf("done. (%ld%%)\n",100L - (100L*hdr->size)/hdr->length);
  1601. X}
  1602. X
  1603. X/*
  1604. X * Non-repeat compression - text is passed through normally, except that a
  1605. X * run of more than two is encoded as:
  1606. X * 
  1607. X * <char> <DLE> <count>
  1608. X * 
  1609. X * Special case: a count of zero indicates that the DLE is really a DLE, not a
  1610. X * repeat marker.
  1611. X */
  1612. X
  1613. Xint
  1614. Xgetc_ncr(f)            /* get bytes with collapsed runs */
  1615. X    FILE           *f;    /* file to get from */
  1616. X{
  1617. X    static int      lastc;    /* value returned on last call */
  1618. X    static int      repcnt;    /* repetition counter */
  1619. X    static int      c;    /* latest value seen */
  1620. X
  1621. X    switch (state) {    /* depends on our state */
  1622. X    case NOHIST:        /* no relevant history */
  1623. X        state = SENTCHAR;
  1624. X        return lastc = getch(f);    /* remember the value next
  1625. X                         * time */
  1626. X
  1627. X    case SENTCHAR:        /* char was sent. look ahead */
  1628. X        switch (lastc) {/* action depends on char */
  1629. X        case DLE:    /* if we sent a real DLE */
  1630. X            state = NOHIST;    /* then start over again */
  1631. X            return 0;    /* but note that the DLE was real */
  1632. X
  1633. X        case EOF:    /* EOF is always a special case */
  1634. X            return EOF;
  1635. X
  1636. X        default:    /* else test for a repeat */
  1637. X            for (repcnt = 1; (c = getch(f)) == lastc && repcnt < 255; repcnt++);
  1638. X            /* find end of run */
  1639. X
  1640. X            switch (repcnt) {    /* action depends on run size */
  1641. X            case 1:/* not a repeat */
  1642. X                return lastc = c;    /* but remember value
  1643. X                             * next time */
  1644. X
  1645. X            case 2:/* a repeat, but too short */
  1646. X                state = SENDNEWC;    /* send the second one
  1647. X                             * next time */
  1648. X                return lastc;
  1649. X
  1650. X            default:    /* a run - compress it */
  1651. X                state = SENDCNT;    /* send repeat count
  1652. X                             * next time */
  1653. X                return DLE;    /* send repeat marker this
  1654. X                         * time */
  1655. X            }
  1656. X        }
  1657. X
  1658. X    case SENDNEWC:        /* send second char of short run */
  1659. X        state = SENTCHAR;
  1660. X        return lastc = c;
  1661. X
  1662. X    case SENDCNT:        /* sent DLE, now send count */
  1663. X        state = SENDNEWC;
  1664. X        return repcnt;
  1665. X
  1666. X    default:
  1667. X        abort("Bug - bad ncr state\n");
  1668. X    }
  1669. X}
  1670. X
  1671. Xint
  1672. Xgetch(f)            /* special get char for packing */
  1673. X    FILE           *f;    /* file to get from */
  1674. X{
  1675. X    int        c;    /* a char from the file */
  1676. X#if    !DOS
  1677. X    static int      cr = 0;    /* add \r before \n ? */
  1678. X
  1679. X    if (cr) {
  1680. X        c = '\n';
  1681. X#if    MTS
  1682. X        c = toascii(c);
  1683. X#endif
  1684. X        crcval = addcrc(crcval, c);
  1685. X        stdlen++;
  1686. X        cr = 0;
  1687. X        return (c);
  1688. X    }
  1689. X#endif
  1690. X
  1691. X    if ((c = fgetc(f)) != EOF) {    /* if not the end of file */
  1692. X#if    !DOS
  1693. X        if (!image && (c == '\n')) {
  1694. X            c = '\r';
  1695. X            cr = 1;
  1696. X        }
  1697. X#endif
  1698. X#if    MTS
  1699. X        if (!image)
  1700. X            c = toascii(c);
  1701. X#endif
  1702. X        crcval = addcrc(crcval, c);    /* then update CRC check
  1703. X                         * value */
  1704. X        stdlen++;    /* and bump length counter */
  1705. X    }
  1706. X    return c;
  1707. X}
  1708. X
  1709. Xvoid
  1710. Xputc_pak(c, f)            /* put a packed byte into archive */
  1711. X    char            c;    /* byte to put */
  1712. X    FILE           *f;    /* archive to put it in */
  1713. X{
  1714. X    unsigned char        code();
  1715. X    putc_tst(code(c), f);    /* put encoded byte, with checks */
  1716. X}
  1717. ________This_Is_The_END________
  1718. if test `wc -c < arcpack.c` -ne     7376; then
  1719.     echo 'shar: arcpack.c was damaged during transit (should have been     7376 bytes)'
  1720. fi
  1721. fi        ; : end of overwriting check
  1722. echo 'x - arcrun.c'
  1723. if test -f arcrun.c; then echo 'shar: not overwriting arcrun.c'; else
  1724. sed 's/^X//' << '________This_Is_The_END________' > arcrun.c
  1725. X/*
  1726. X * $Header: arcrun.c,v 1.3 88/06/01 19:57:16 hyc Locked $
  1727. X */
  1728. X
  1729. X/*
  1730. X * ARC - Archive utility - ARCRUN
  1731. X * 
  1732. X * Version 1.20, created on 03/24/86 at 19:34:31
  1733. X * 
  1734. X * (C) COPYRIGHT 1985,85 by System Enhancement Associates; ALL RIGHTS RESERVED
  1735. X * 
  1736. X * By:  Thom Henderson
  1737. X * 
  1738. X * Description: This file contains the routines used to "run" a file which is
  1739. X * stored in an archive.  At present, all we really do is (a) extract a
  1740. X * temporary file, (b) give its name as a system command, and then (c) delete
  1741. X * the file.
  1742. X * 
  1743. X * Language: Computer Innovations Optimizing C86
  1744. X */
  1745. X#include <stdio.h>
  1746. X#include "arc.h"
  1747. X
  1748. Xvoid    rempath(), openarc(), closearc(), abort();
  1749. Xchar    *strcat();
  1750. X
  1751. Xvoid
  1752. Xrunarc(num, arg)        /* run file from archive */
  1753. X    int             num;    /* number of arguments */
  1754. X    char           *arg[];    /* pointers to arguments */
  1755. X{
  1756. X    struct heads    hdr;    /* file header */
  1757. X    char           *makefnam();    /* filename fixer */
  1758. X    char            buf[STRLEN];    /* filename buffer */
  1759. X    FILE           *fopen();/* file opener */
  1760. X    int             runfile();
  1761. X    char           *dummy[2];
  1762. X
  1763. X    dummy[0]="dummy";
  1764. X    dummy[1]=NULL;
  1765. X    rempath(num, arg);    /* strip off paths */
  1766. X
  1767. X    openarc(0);        /* open archive for reading */
  1768. X
  1769. X    if (num) {        /* if files were named */
  1770. X        while (readhdr(&hdr, arc)) {    /* while more files to check */
  1771. X            if (match(hdr.name, makefnam(arg[0], ".*", buf)))
  1772. X                runfile(&hdr, num, arg);
  1773. X            else
  1774. X                fseek(arc, hdr.size, 1);
  1775. X        }
  1776. X    } else
  1777. X        while (readhdr(&hdr, arc))    /* else run all files */
  1778. X            runfile(&hdr, 1, dummy);
  1779. X
  1780. X    closearc(0);        /* close archive after changes */
  1781. X}
  1782. X
  1783. Xstatic          int
  1784. Xrunfile(hdr, num, arg)        /* run a file */
  1785. X    struct heads   *hdr;    /* pointer to header data */
  1786. X    int             num;    /* number of arguments */
  1787. X    char           *arg[];    /* pointers to arguments */
  1788. X{
  1789. X    FILE           *tmp, *fopen();    /* temporary file */
  1790. X    char           *dir, *gcdir();    /* directory stuff */
  1791. X    char            buf[STRLEN], *makefnam();    /* temp file name, fixer */
  1792. X#if    DOS
  1793. X    char        nbuf[64], *i, *rindex();
  1794. X#endif
  1795. X#if    !GEMDOS
  1796. X    int             n;    /* index */
  1797. X    char            sys[STRLEN];    /* invocation command buffer */
  1798. X#endif
  1799. X
  1800. X    /* makefnam("$ARCTEMP",hdr->name,buf); */
  1801. X#if    UNIX
  1802. X    sprintf(buf, "%s.RUN", arctemp);
  1803. X    strcpy(sys, buf);
  1804. X#else
  1805. X    strcpy(nbuf, arctemp);
  1806. X    makefnam(nbuf,hdr->name,buf);
  1807. X    i = rindex(buf,'.');
  1808. X#endif
  1809. X#if    MSDOS
  1810. X    if (!strcmp(i, ".BAS")) {
  1811. X        strcpy(sys, "BASICA ");
  1812. X        strcat(sys, buf);
  1813. X    }
  1814. X    else if (!strcmp(i, ".BAT")
  1815. X         || !strcmp(i, ".COM")
  1816. X         || !strcmp(i, ".EXE"))
  1817. X        strcpy(sys, buf);
  1818. X
  1819. X    else {
  1820. X        if (warn) {
  1821. X            printf("File %s is not a .BAS, .BAT, .COM, or .EXE\n",
  1822. X                   hdr->name);
  1823. X            nerrs++;
  1824. X        }
  1825. X        fseek(arc, hdr->size, 1);    /* skip this file */
  1826. X        return;
  1827. X    }
  1828. X#endif
  1829. X#if    GEMDOS
  1830. X      if (strcmp(i, ".PRG")
  1831. X              && strcmp(i, ".TTP")
  1832. X              && strcmp(i, ".TOS"))
  1833. X      {
  1834. X              if (warn) {
  1835. X                      printf("File %s is not a .PRG, .TOS, or .TTP\n",
  1836. X                              hdr->name);
  1837. X                      nerrs++;
  1838. X              }
  1839. X              fseek(arc, hdr->size, 1);       /* skip this file */
  1840. X              return;
  1841. X      }
  1842. X#endif
  1843. X
  1844. X    if (warn)
  1845. X        if (tmp = fopen(buf, "r"))
  1846. X            abort("Temporary file %s already exists", buf);
  1847. X    if (!(tmp = fopen(buf, "wb")))
  1848. X        abort("Unable to create temporary file %s", buf);
  1849. X
  1850. X    if (note)
  1851. X        printf("Invoking file: %s\n", hdr->name);
  1852. X
  1853. X    dir = gcdir("");    /* see where we are */
  1854. X    unpack(arc, tmp, hdr);    /* unpack the entry */
  1855. X    fclose(tmp);        /* release the file */
  1856. X    chmod(buf, "700");    /* make it executable */
  1857. X#if    GEMDOS
  1858. X    execve(buf, arg, NULL);
  1859. X#else
  1860. X    for (n = 1; n < num; n++) {    /* add command line arguments */
  1861. X        strcat(sys, " ");
  1862. X        strcat(sys, arg[n]);
  1863. X    }
  1864. X    system(buf);        /* try to invoke it */
  1865. X#endif
  1866. X    chdir(dir);
  1867. X    free(dir);        /* return to whence we started */
  1868. X    if (unlink(buf) && warn) {
  1869. X        printf("Cannot unsave temporary file %s\n", buf);
  1870. X        nerrs++;
  1871. X    }
  1872. X}
  1873. ________This_Is_The_END________
  1874. if test `wc -c < arcrun.c` -ne     3838; then
  1875.     echo 'shar: arcrun.c was damaged during transit (should have been     3838 bytes)'
  1876. fi
  1877. fi        ; : end of overwriting check
  1878. echo 'x - arcs.h'
  1879. if test -f arcs.h; then echo 'shar: not overwriting arcs.h'; else
  1880. sed 's/^X//' << '________This_Is_The_END________' > arcs.h
  1881. X/*
  1882. X * $Header: arcs.h,v 1.2 88/04/17 18:53:19 hyc Exp $
  1883. X */
  1884. X
  1885. X/*
  1886. X * ARC - Archive utility - Archive file header format
  1887. X * 
  1888. X * Version 2.12, created on 12/17/85 at 14:40:26
  1889. X * 
  1890. X * (C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED
  1891. X * 
  1892. X * By:  Thom Henderson
  1893. X * 
  1894. X * Description: This file defines the format of an archive file header,
  1895. X * excluding the archive marker and the header version number.
  1896. X * 
  1897. X * Each entry in an archive begins with a one byte archive marker, which is set
  1898. X * to 26.  The marker is followed by a one byte header type code, from zero
  1899. X * to 7.
  1900. X * 
  1901. X * If the header type code is zero, then it is an end marker, and no more data
  1902. X * should be read from the archive.
  1903. X * 
  1904. X * If the header type code is in the range 2 to 7, then it is followed by a
  1905. X * standard archive header, which is defined below.
  1906. X * 
  1907. X * If the header type code is one, then it is followed by an older format
  1908. X * archive header.  The older format header does not contain the true length.
  1909. X * A header should be read for a length of sizeof(struct heads)-sizeof(long).
  1910. X * Then set length equal to size and change the header version to 2.
  1911. X * 
  1912. X * Programming note: The crc value given in the header is based on the unpacked
  1913. X * data.
  1914. X * 
  1915. X * Language: Computer Innovations Optimizing C86
  1916. X */
  1917. X
  1918. Xstruct heads {            /* archive entry header format */
  1919. X    char    name[FNLEN];        /* file name */
  1920. X            long size;        /* size of file, in bytes */
  1921. X    unsigned    short date;    /* creation date */
  1922. X    unsigned    short time;    /* creation time */
  1923. X                short crc;    /* cyclic redundancy check */
  1924. X                long length;    /* true file length */
  1925. X};
  1926. ________This_Is_The_END________
  1927. if test `wc -c < arcs.h` -ne     1645; then
  1928.     echo 'shar: arcs.h was damaged during transit (should have been     1645 bytes)'
  1929. fi
  1930. fi        ; : end of overwriting check
  1931. echo 'x - arcsq.c'
  1932. if test -f arcsq.c; then echo 'shar: not overwriting arcsq.c'; else
  1933. sed 's/^X//' << '________This_Is_The_END________' > arcsq.c
  1934. X/*
  1935. X * $Header: arcsq.c,v 1.2 88/06/02 16:27:38 hyc Locked $
  1936. X */
  1937. X
  1938. X/*
  1939. X * ARC - Archive utility - ARCSQ 
  1940. X *
  1941. X * Version 3.10, created on 01/30/86 at 20:10:46
  1942. X *
  1943. X * (C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED 
  1944. X *
  1945. X * By:  Thom Henderson 
  1946. X *
  1947. X * Description: This file contains the routines used to squeeze a file when
  1948. X * placing it in an archive. 
  1949. X *
  1950. X * Language: Computer Innovations Optimizing C86 
  1951. X *
  1952. X * Programming notes: Most of the routines used for the Huffman squeezing
  1953. X * algorithm were lifted from the SQ program by Dick Greenlaw, as adapted to
  1954. X * CI-C86 by Robert J. Beilstein. 
  1955. X */
  1956. X#include <stdio.h>
  1957. X#include "arc.h"
  1958. X
  1959. X/* stuff for Huffman squeezing */
  1960. X
  1961. X#define TRUE 1
  1962. X#define FALSE 0
  1963. X#define ERROR (-1)
  1964. X#define SPEOF 256        /* special endfile token */
  1965. X#define NOCHILD (-1)        /* marks end of path through tree */
  1966. X#define NUMVALS 257        /* 256 data values plus SPEOF */
  1967. X#define NUMNODES (NUMVALS+NUMVALS-1)    /* number of nodes */
  1968. X#define MAXCOUNT (unsigned short) 65535    /* biggest unsigned integer */
  1969. X
  1970. X/*
  1971. X * The following array of structures are the nodes of the binary trees. The
  1972. X * first NUMVALS nodes become the leaves of the final tree and represent the
  1973. X * values of the data bytes being encoded and the special endfile, SPEOF. The
  1974. X * remaining nodes become the internal nodes of the final tree. 
  1975. X */
  1976. X
  1977. Xstruct nd {            /* shared by unsqueezer */
  1978. X    unsigned short    weight;    /* number of appearances */
  1979. X    short        tdepth;    /* length on longest path in tree */
  1980. X    short        lchild, rchild;    /* indices to next level */
  1981. X}               node[NUMNODES];    /* use large buffer */
  1982. X
  1983. Xstatic int      dctreehd;    /* index to head of final tree */
  1984. X
  1985. X/*
  1986. X * This is the encoding table: The bit strings have first bit in low bit.
  1987. X * Note that counts were scaled so code fits unsigned integer. 
  1988. X */
  1989. X
  1990. Xstatic int      codelen[NUMVALS];    /* number of bits in code */
  1991. Xstatic unsigned short code[NUMVALS];    /* code itself, right adjusted */
  1992. Xstatic unsigned short tcode;    /* temporary code value */
  1993. Xstatic long     valcount[NUMVALS];    /* actual count of times seen */
  1994. X
  1995. X/* Variables used by encoding process */
  1996. X
  1997. Xstatic int      curin;        /* value currently being encoded */
  1998. Xstatic int      cbitsrem;    /* # of code string bits left */
  1999. Xstatic unsigned short ccode;    /* current code right justified */
  2000. X
  2001. Xint 
  2002. Xinit_sq()
  2003. X{                /* prepare for scanning pass */
  2004. X    int             i;    /* node index */
  2005. X
  2006. X    /*
  2007. X     * Initialize all nodes to single element binary trees with zero
  2008. X     * weight and depth. 
  2009. X     */
  2010. X
  2011. X    for (i = 0; i < NUMNODES; ++i) {
  2012. X        node[i].weight = 0;
  2013. X        node[i].tdepth = 0;
  2014. X        node[i].lchild = NOCHILD;
  2015. X        node[i].rchild = NOCHILD;
  2016. X    }
  2017. X
  2018. X    for (i = 0; i < NUMVALS; i++)
  2019. X        valcount[i] = 0;
  2020. X}
  2021. X
  2022. Xint 
  2023. Xscan_sq(c)            /* add a byte to the tables */
  2024. X    int             c;    /* byte to add */
  2025. X{
  2026. X    unsigned short *wp;    /* speeds up weight counting */
  2027. X
  2028. X    /* Build frequency info in tree */
  2029. X
  2030. X    if (c == EOF)        /* it's traditional */
  2031. X        c = SPEOF;    /* dumb, but traditional */
  2032. X
  2033. X    if (*(wp = &node[c].weight) != MAXCOUNT)
  2034. X        ++(*wp);    /* bump weight counter */
  2035. X
  2036. X    valcount[c]++;        /* bump byte counter */
  2037. X}
  2038. X
  2039. Xlong 
  2040. Xpred_sq()
  2041. X{                /* predict size of squeezed file */
  2042. X    int             i;
  2043. X    int             btlist[NUMVALS];    /* list of intermediate
  2044. X                         * b-trees */
  2045. X    int             listlen;/* length of btlist */
  2046. X    unsigned short    ceiling;/* limit for scaling */
  2047. X    long            size = 0;    /* predicted size */
  2048. X    int             numnodes;    /* # of nodes in simplified tree */
  2049. X    int             scale();
  2050. X    int             heap();
  2051. X    int             bld_tree();
  2052. X    int             buildenc();
  2053. X    int             init_enc();
  2054. X
  2055. X    scan_sq(EOF);        /* signal end of input */
  2056. X
  2057. X    ceiling = MAXCOUNT;
  2058. X
  2059. X    /* Keep trying to scale and encode */
  2060. X
  2061. X    do {
  2062. X        scale(ceiling);
  2063. X        ceiling /= 2;    /* in case we rescale */
  2064. X
  2065. X        /*
  2066. X         * Build list of single node binary trees having leaves for
  2067. X         * the input values with non-zero counts 
  2068. X         */
  2069. X
  2070. X        for (i = listlen = 0; i < NUMVALS; ++i) {
  2071. X            if (node[i].weight != 0) {
  2072. X                node[i].tdepth = 0;
  2073. X                btlist[listlen++] = i;
  2074. X            }
  2075. X        }
  2076. X
  2077. X        /*
  2078. X         * Arrange list of trees into a heap with the entry indexing
  2079. X         * the node with the least weight at the top. 
  2080. X         */
  2081. X
  2082. X        heap(btlist, listlen);
  2083. X
  2084. X        /* Convert the list of trees to a single decoding tree */
  2085. X
  2086. X        bld_tree(btlist, listlen);
  2087. X
  2088. X        /* Initialize the encoding table */
  2089. X
  2090. X        init_enc();
  2091. X
  2092. X        /*
  2093. X         * Try to build encoding table. Fail if any code is > 16 bits
  2094. X         * long. 
  2095. X         */
  2096. X    } while (buildenc(0, dctreehd) == ERROR);
  2097. X
  2098. X    /* Initialize encoding variables */
  2099. X
  2100. X    cbitsrem = 0;        /* force initial read */
  2101. X    curin = 0;        /* anything but endfile */
  2102. X
  2103. X    for (i = 0; i < NUMVALS; i++)    /* add bits for each code */
  2104. X        size += valcount[i] * codelen[i];
  2105. X
  2106. X    size = (size + 7) / 8;    /* reduce to number of bytes */
  2107. X
  2108. X    numnodes = dctreehd < NUMVALS ? 0 : dctreehd - (NUMVALS - 1);
  2109. X
  2110. X    size += sizeof(short) + 2 * numnodes * sizeof(short);
  2111. X
  2112. X    return size;
  2113. X}
  2114. X
  2115. X/*
  2116. X * The count of number of occurrances of each input value have already been
  2117. X * prevented from exceeding MAXCOUNT. Now we must scale them so that their
  2118. X * sum doesn't exceed ceiling and yet no non-zero count can become zero. This
  2119. X * scaling prevents errors in the weights of the interior nodes of the
  2120. X * Huffman tree and also ensures that the codes will fit in an unsigned
  2121. X * integer. Rescaling is used if necessary to limit the code length. 
  2122. X */
  2123. X
  2124. Xstatic int 
  2125. Xscale(ceil)
  2126. X    unsigned short    ceil;    /* upper limit on total weight */
  2127. X{
  2128. X    register int    i;
  2129. X    int             ovflw, divisor;
  2130. X    unsigned short    w, sum;
  2131. X    unsigned char   increased;    /* flag */
  2132. X
  2133. X    do {
  2134. X        for (i = sum = ovflw = 0; i < NUMVALS; ++i) {
  2135. X            if (node[i].weight > (ceil - sum))
  2136. X                ++ovflw;
  2137. X            sum += node[i].weight;
  2138. X        }
  2139. X
  2140. X        divisor = ovflw + 1;
  2141. X
  2142. X        /* Ensure no non-zero values are lost */
  2143. X
  2144. X        increased = FALSE;
  2145. X        for (i = 0; i < NUMVALS; ++i) {
  2146. X            w = node[i].weight;
  2147. X            if (w < divisor && w != 0) {    /* Don't fail to provide
  2148. X                             * a code if it's used
  2149. X                             * at all */
  2150. X
  2151. X                node[i].weight = divisor;
  2152. X                increased = TRUE;
  2153. X            }
  2154. X        }
  2155. X    } while (increased);
  2156. X
  2157. X    /* Scaling factor chosen, now scale */
  2158. X
  2159. X    if (divisor > 1)
  2160. X        for (i = 0; i < NUMVALS; ++i)
  2161. X            node[i].weight /= divisor;
  2162. X}
  2163. X
  2164. X/*
  2165. X * heap() and adjust() maintain a list of binary trees as a heap with the top
  2166. X * indexing the binary tree on the list which has the least weight or, in
  2167. X * case of equal weights, least depth in its longest path. The depth part is
  2168. X * not strictly necessary, but tends to avoid long codes which might provoke
  2169. X * rescaling. 
  2170. X */
  2171. X
  2172. Xstatic int 
  2173. Xheap(list, length)
  2174. X    int             list[], length;
  2175. X{
  2176. X    register int    i;
  2177. X    int             adjust();
  2178. X
  2179. X    for (i = (length - 2) / 2; i >= 0; --i)
  2180. X        adjust(list, i, length - 1);
  2181. X}
  2182. X
  2183. X/* Make a heap from a heap with a new top */
  2184. X
  2185. Xstatic int 
  2186. Xadjust(list, top, bottom)
  2187. X    int             list[], top, bottom;
  2188. X{
  2189. X    register int    k, temp;
  2190. X    int             cmptrees();
  2191. X
  2192. X    k = 2 * top + 1;    /* left child of top */
  2193. X    temp = list[top];    /* remember root node of top tree */
  2194. X
  2195. X    if (k <= bottom) {
  2196. X        if (k < bottom && cmptrees(list[k], list[k + 1]))
  2197. X            ++k;
  2198. X
  2199. X        /* k indexes "smaller" child (in heap of trees) of top */
  2200. X        /* now make top index "smaller" of old top and smallest child */
  2201. X
  2202. X        if (cmptrees(temp, list[k])) {
  2203. X            list[top] = list[k];
  2204. X            list[k] = temp;
  2205. X
  2206. X            /* Make the changed list a heap */
  2207. X
  2208. X            adjust(list, k, bottom);    /* recursive */
  2209. X        }
  2210. X    }
  2211. X}
  2212. X
  2213. X/*
  2214. X * Compare two trees, if a > b return true, else return false. Note
  2215. X * comparison rules in previous comments. 
  2216. X */
  2217. X
  2218. Xstatic int 
  2219. Xcmptrees(a, b)
  2220. X    int             a, b;    /* root nodes of trees */
  2221. X{
  2222. X    if (node[a].weight > node[b].weight)
  2223. X        return TRUE;
  2224. X    if (node[a].weight == node[b].weight)
  2225. X        if (node[a].tdepth > node[b].tdepth)
  2226. X            return TRUE;
  2227. X    return FALSE;
  2228. X}
  2229. X
  2230. X/*
  2231. X * HUFFMAN ALGORITHM: develops the single element trees into a single binary
  2232. X * tree by forming subtrees rooted in interior nodes having weights equal to
  2233. X * the sum of weights of all their descendents and having depth counts
  2234. X * indicating the depth of their longest paths. 
  2235. X *
  2236. X * When all trees have been formed into a single tree satisfying the heap
  2237. X * property (on weight, with depth as a tie breaker) then the binary code
  2238. X * assigned to a leaf (value to be encoded) is then the series of left (0)
  2239. X * and right (1) paths leading from the root to the leaf. Note that trees are
  2240. X * removed from the heaped list by moving the last element over the top
  2241. X * element and reheaping the shorter list. 
  2242. X */
  2243. X
  2244. Xstatic int 
  2245. Xbld_tree(list, len)
  2246. X    int             list[];
  2247. Xint             len;
  2248. X{
  2249. X    register int    freenode;    /* next free node in tree */
  2250. X    register struct nd *frnp;    /* free node pointer */
  2251. X    int             lch, rch;    /* temps for left, right children */
  2252. X    int             maxchar();
  2253. X
  2254. X    /*
  2255. X     * Initialize index to next available (non-leaf) node. Lower numbered
  2256. X     * nodes correspond to leaves (data values). 
  2257. X     */
  2258. X
  2259. X    freenode = NUMVALS;
  2260. X
  2261. X    while (len > 1) {    /* Take from list two btrees with least
  2262. X                 * weight and build an interior node pointing
  2263. X                 * to them. This forms a new tree. */
  2264. X
  2265. X        lch = list[0];    /* This one will be left child */
  2266. X
  2267. X        /* delete top (least) tree from the list of trees */
  2268. X
  2269. X        list[0] = list[--len];
  2270. X        adjust(list, 0, len - 1);
  2271. X
  2272. X        /* Take new top (least) tree. Reuse list slot later */
  2273. X
  2274. X        rch = list[0];    /* This one will be right child */
  2275. X
  2276. X        /*
  2277. X         * Form new tree from the two least trees using a free node
  2278. X         * as root. Put the new tree in the list. 
  2279. X         */
  2280. X
  2281. X        frnp = &node[freenode];    /* address of next free node */
  2282. X        list[0] = freenode++;    /* put at top for now */
  2283. X        frnp->lchild = lch;
  2284. X        frnp->rchild = rch;
  2285. X        frnp->weight = node[lch].weight + node[rch].weight;
  2286. X        frnp->tdepth = 1 + maxchar(node[lch].tdepth, node[rch].tdepth);
  2287. X
  2288. X        /* reheap list  to get least tree at top */
  2289. X
  2290. X        adjust(list, 0, len - 1);
  2291. X    }
  2292. X    dctreehd = list[0];    /* head of final tree */
  2293. X}
  2294. X
  2295. Xstatic int 
  2296. Xmaxchar(a, b)
  2297. X{
  2298. X    return a > b ? a : b;
  2299. X}
  2300. X
  2301. Xstatic int 
  2302. Xinit_enc()
  2303. X{
  2304. X    register int    i;
  2305. X
  2306. X    /* Initialize encoding table */
  2307. X
  2308. X    for (i = 0; i < NUMVALS; ++i)
  2309. X        codelen[i] = 0;
  2310. X}
  2311. X
  2312. X/*
  2313. X * Recursive routine to walk the indicated subtree and level and maintain the
  2314. X * current path code in bstree. When a leaf is found the entire code string
  2315. X * and length are put into the encoding table entry for the leaf's data value
  2316. X * . 
  2317. X *
  2318. X * Returns ERROR if codes are too long. 
  2319. X */
  2320. X
  2321. Xstatic int 
  2322. Xbuildenc(level, root)
  2323. X    int             level;    /* level of tree being examined, from zero */
  2324. X    int             root;    /* root of subtree is also data value if leaf */
  2325. X{
  2326. X    register int    l, r;
  2327. X
  2328. X    l = node[root].lchild;
  2329. X    r = node[root].rchild;
  2330. X
  2331. X    if (l == NOCHILD && r == NOCHILD) {    /* Leaf. Previous path
  2332. X                         * determines bit string code
  2333. X                         * of length level (bits 0 to
  2334. X                         * level - 1). Ensures unused
  2335. X                         * code bits are zero. */
  2336. X
  2337. X        codelen[root] = level;
  2338. X        code[root] = tcode & (((unsigned short    ) ~0) >> (16 - level));
  2339. X        return (level > 16) ? ERROR : 0;
  2340. X    } else {
  2341. X        if (l != NOCHILD) {    /* Clear path bit and continue deeper */
  2342. X
  2343. X            tcode &= ~(1 << level);
  2344. X            if (buildenc(level + 1, l) == ERROR)
  2345. X                return ERROR;    /* pass back bad statuses */
  2346. X        }
  2347. X        if (r != NOCHILD) {    /* Set path bit and continue deeper */
  2348. X
  2349. X            tcode |= 1 << level;
  2350. X            if (buildenc(level + 1, r) == ERROR)
  2351. X                return ERROR;    /* pass back bad statuses */
  2352. X        }
  2353. X    }
  2354. X    return NULL;        /* it worked if we reach here */
  2355. X}
  2356. X
  2357. Xstatic int 
  2358. Xput_int(n, f)            /* output an integer */
  2359. X    short        n;    /* integer to output */
  2360. X    FILE           *f;    /* file to put it to */
  2361. X{
  2362. X    putc_pak(n & 0xff, f);    /* first the low byte */
  2363. X    putc_pak(n >> 8, f);    /* then the high byte */
  2364. X}
  2365. X
  2366. X/* Write out the header of the compressed file */
  2367. X
  2368. Xstatic long 
  2369. Xwrt_head(ob)
  2370. X    FILE           *ob;
  2371. X{
  2372. X    register int    l, r;
  2373. X    int             i, k;
  2374. X    int             numnodes;    /* # of nodes in simplified tree */
  2375. X
  2376. X    /*
  2377. X     * Write out a simplified decoding tree. Only the interior nodes are
  2378. X     * written. When a child is a leaf index (representing a data value)
  2379. X     * it is recoded as -(index + 1) to distinguish it from interior
  2380. X     * indexes which are recoded as positive indexes in the new tree. 
  2381. X     *
  2382. X     * Note that this tree will be empty for an empty file. 
  2383. X     */
  2384. X
  2385. X    numnodes = dctreehd < NUMVALS ? 0 : dctreehd - (NUMVALS - 1);
  2386. X    put_int(numnodes, ob);
  2387. X
  2388. X    for (k = 0, i = dctreehd; k < numnodes; ++k, --i) {
  2389. X        l = node[i].lchild;
  2390. X        r = node[i].rchild;
  2391. X        l = l < NUMVALS ? -(l + 1) : dctreehd - l;
  2392. X        r = r < NUMVALS ? -(r + 1) : dctreehd - r;
  2393. X        put_int(l, ob);
  2394. X        put_int(r, ob);
  2395. X    }
  2396. X
  2397. X    return sizeof(short) + numnodes * 2 * sizeof(short);
  2398. X}
  2399. X
  2400. X/*
  2401. X * Get an encoded byte or EOF. Reads from specified stream AS NEEDED. 
  2402. X *
  2403. X * There are two unsynchronized bit-byte relationships here. The input stream
  2404. X * bytes are converted to bit strings of various lengths via the static
  2405. X * variables named c... These bit strings are concatenated without padding to
  2406. X * become the stream of encoded result bytes, which this function returns one
  2407. X * at a time. The EOF (end of file) is converted to SPEOF for convenience and
  2408. X * encoded like any other input value. True EOF is returned after that. 
  2409. X */
  2410. X
  2411. Xstatic int 
  2412. Xgethuff(ib)            /* Returns bytes except for EOF */
  2413. X    FILE           *ib;
  2414. X{
  2415. X    int             rbyte;    /* Result byte value */
  2416. X    int             need;    /* number of bits */
  2417. X
  2418. X    rbyte = 0;
  2419. X    need = 8;        /* build one byte per call */
  2420. X
  2421. X    /*
  2422. X     * Loop to build a byte of encoded data. Initialization forces read
  2423. X     * the first time. 
  2424. X     */
  2425. X
  2426. Xloop:
  2427. X    if (cbitsrem >= need) {    /* if current code is big enough */
  2428. X        if (need == 0)
  2429. X            return rbyte;
  2430. X
  2431. X        rbyte |= ccode << (8 - need);    /* take what we need */
  2432. X        ccode >>= need;    /* and leave the rest */
  2433. X        cbitsrem -= need;
  2434. X        return rbyte & 0xff;
  2435. X    }
  2436. X    /* We need more than current code */
  2437. X
  2438. X    if (cbitsrem > 0) {
  2439. X        rbyte |= ccode << (8 - need);    /* take what there is */
  2440. X        need -= cbitsrem;
  2441. X    }
  2442. X    /* No more bits in current code string */
  2443. X
  2444. X    if (curin == SPEOF) {    /* The end of file token has been encoded. If
  2445. X                 * result byte has data return it and do EOF
  2446. X                 * next time. */
  2447. X
  2448. X        cbitsrem = 0;
  2449. X        return (need == 8) ? EOF : rbyte + 0;
  2450. X    }
  2451. X    /* Get an input byte */
  2452. X
  2453. X    if ((curin = getc_ncr(ib)) == EOF)
  2454. X        curin = SPEOF;    /* convenient for encoding */
  2455. X
  2456. X    ccode = code[curin];    /* get the new byte's code */
  2457. X    cbitsrem = codelen[curin];
  2458. X
  2459. X    goto loop;
  2460. X}
  2461. X
  2462. X/*
  2463. X * This routine is used to perform the actual squeeze operation.  It can only
  2464. X * be called after the file has been scanned.  It returns the true length of
  2465. X * the squeezed entry. 
  2466. X */
  2467. X
  2468. Xlong 
  2469. Xfile_sq(f, t)            /* squeeze a file into an archive */
  2470. X    FILE           *f;    /* file to squeeze */
  2471. X    FILE           *t;    /* archive to receive file */
  2472. X{
  2473. X    int             c;    /* one byte of squeezed data */
  2474. X    long            size;    /* size after squeezing */
  2475. X
  2476. X    size = wrt_head(t);    /* write out the decode tree */
  2477. X
  2478. X    while ((c = gethuff(f)) != EOF) {
  2479. X        putc_pak(c, t);
  2480. X        size++;
  2481. X    }
  2482. X
  2483. X    return size;        /* report true size */
  2484. X}
  2485. ________This_Is_The_END________
  2486. if test `wc -c < arcsq.c` -ne    14613; then
  2487.     echo 'shar: arcsq.c was damaged during transit (should have been    14613 bytes)'
  2488. fi
  2489. fi        ; : end of overwriting check
  2490. exit 0
  2491.  
  2492. -- 
  2493. Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
  2494.  
  2495.  
  2496.